;  PROGRAM:  COMPARE
;  AUTHOR:  Richard Conn
;  VERSION:  1.4
;  DATE:  6 JAN 83
;  PREVIOUS VERSIONS:  1.3 (19 DEC 82)
;  PREVIOUS VERSIONS:  1.2 (8 DEC 82), 1.1 (21 JULY 82), 1.0 (11 JULY 82)
VERS	EQU	14

;
;	This program is Copyright (c) 1982, 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;


;
;	COMPARE is designed to provide the user with a convenient method
; to compare the contents of two files.  It is invoked by one of two basic
; forms:
;
;		COMPARE filename.typ
; or
;		COMPARE file1.typ file2.typ
;
;	The first form compares the file named "filename.typ" on drive A:
; to the file of the same name on drive B:; the second form compares the
; file named "file1.typ" on drive A: to the file named "file2.typ" on drive
; B:.
;

;  CP/M Constants
CPM	equ	0	; CP/M Warm Boot
BUFF	equ	CPM+80H	; Temporary Buffer
CR	equ	0DH
LF	equ	0AH

;  CRC ROUTINES
	EXT	CRCCLR,CRCUPD,CRCDONE

;  SYSLIB ROUTINES
	EXT	COMPHD,BDOS,INITFCB,LOGUD,RETUD
	EXT	F$OPEN,F$CLOSE,F$READ
	EXT	CAPS,CIN,COUT,CRLF
	EXT	PADC,MOVEB,PRINT
	EXT	ZGPINS,ZFNAME

;
;  Branch to Start of Program
;
	JMP	START

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	0FFH	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	0FFH	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	0	; CLASS 0
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  Start of Program
;
START:
	CALL	ZGPINS	; INIT ZCPR2 BUFFERS
	XRA	A	; SET NO MULTIPLE RUN
	STA	MULT
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; SAVE DISK
	STA	CDISK
	MOV	A,C	; SAVE USER
	STA	CUSER
	LXI	H,BUFF	; PROCESS OPTIONS IN BUFFER
	MOV	A,M	; GET CHAR COUNT
	INX	H	; PT TO FIRST CHAR
	PUSH	H	; SAVE PTR TO FIRST CHAR
	ADD	L	; HL=HL+A
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	MVI	M,0	; STORE ENDING ZERO
	POP	H	; GET PTR TO FIRST CHAR
	LXI	D,INLINE	; PT TO INPUT LINE BUFFER
	PUSH	D	; SAVE PTR
START0:
	MOV	A,M	; COPY INPUT LINE SO BUFF MAY BE USED
	STAX	D	; PUT BYTE
	INX	H	; PT TO NEXT
	INX	D
	ORA	A	; EOL?
	JNZ	START0
	POP	H	; PT TO FIRST CHAR
	CALL	SBLANK	; SKIP SPACES
	ORA	A	; EOL?
	JZ	PRHELP	; PRINT HELP IF SO
	CPI	'/'	; ASKING FOR HELP?
	JZ	PRHELP
	LXI	D,FCBS	; PT TO SOURCE FCB
	CALL	ZFNAME	; EXTRACT NAME AND DIRECTORY DATA
	JNZ	START1
UDERR:
	CALL	PRINT
	DB	CR,LF,'Invalid Disk or User -- Aborting',0
	RET
START1:
	MOV	A,B	; SAVE SOURCE DISK
	CPI	0FFH	; CHECK FOR CURRENT
	JNZ	SDISK1
	LDA	CDISK	; SPECIFY CURRENT DISK INSTEAD
	INR	A	; ADD 1 FOR FOLLOWING DECREMENT
SDISK1:
	DCR	A	; DOWN 1 SO RANGE IS 0-F
	STA	SDISK
	MOV	A,C	; SAVE SOURCE USER
	CPI	0FFH	; CHECK FOR CURRENT
	JZ	SUSER0
	CPI	'?'	; WILD IS CURRENT
	JNZ	SUSER1
SUSER0:
	LDA	CUSER	; GET CURRENT USER
SUSER1:
	STA	SUSER
	MOV	A,M	; GET SEPARATION CHAR
	CPI	','	; COMMA IF SECOND NAME SPECIFIED
	JZ	START2
	PUSH	H	; SAVE PTR
	LXI	H,FCBS+1	; NO 2ND NAME, SO SET IT TO SAME AS FIRST
	LXI	D,FCBD+1
	MVI	B,11	; 11 BYTES
	CALL	MOVEB
	LDA	CDISK	; SET DISK AND USER TO CURRENT
	STA	DDISK
	LDA	CUSER
	STA	DUSER
	POP	H	; GET PTR
	JMP	START3
START2:
	INX	H	; PT TO NEXT CHAR AFTER COMMA
	LXI	D,FCBD	; SET DEST FCB
	CALL	ZFNAME	; PROCESS NAME
	JZ	UDERR
	LDA	FCBD+1	; CHECK FOR AMBIGUOUS NAME
	CPI	'?'	; ASSUME ALL IS AMBIGUOUS IF FIRST CHAR IS
	JNZ	NOSET
	PUSH	H	; SAVE PTR
	PUSH	B	; SAVE USER/DISK
	LXI	H,FCBS+1	; SET NAMES THE SAME
	LXI	D,FCBD+1	; COPY SOURCE TO DEST
	MVI	B,11	; 11 BYTES
	CALL	MOVEB
	POP	B	; RESTORE BC
	POP	H	; RESTORE PTR
NOSET:
	MOV	A,B	; GET DISK
	CPI	0FFH
	JNZ	DDISK1
	LDA	CDISK	; SELECT CURRENT DISK IF DEFAULT
	INR	A	; ADD 1 FOR FOLLOWING DECREMENT
DDISK1:
	DCR	A
	STA	DDISK
	MOV	A,C	; GET USER
	CPI	0FFH	; CURRENT?
	JZ	DUSER0
	CPI	'?'	; WILD IS CURRENT
	JNZ	DUSER1
DUSER0:
	LDA	CUSER
DUSER1:
	STA	DUSER	; SET DEST USER
START3:
	CALL	SBLANK	; SKIP SPACES
	CPI	'M'	; MULTIPLE OPTION?
	JNZ	START4
	MVI	A,0FFH	; SET FLAG
	STA	MULT
START4:
	LXI	H,FCBS	; SET UP SOURCE FCB
	CALL	QCHECK	; NO AMBIGUOUS ENTRIES PERMITTED
	LXI	H,FCBD	; SET UP DESTINATION FCB
	CALL	QCHECK	; NO AMBIGUOUS ENTRIES PERMITTED
MLOOP:
	CALL	BANNER	; PRINT BANNER
	LDA	MULT	; MULTIPLE RUNS?
	ORA	A	; 0=NO
	JZ	MLOOP1
	CALL	PRS1	; PRINT SOURCE FILE 1
	CALL	PRS2	; PRINT SOURCE FILE 2
	CALL	PRINT
	DB	CR,LF,'  Change Disks if Desired and Type ^C or A to Abort or '
	DB	'<RETURN> to Continue - ',0
	CALL	CIN	; GET RESPONSE
	CALL	CAPS	; CAPITALIZE
	CPI	3	; ABORT?
	RZ
	CPI	'A'	; ABORT?
	RZ
	MVI	C,13	; RESET DISKS
	CALL	BDOS
MLOOP1:
	CALL	VERIFY	; PERFORM VERIFICATION
	CALL	CRLF	; NEW LINE
	LDA	MULT	; MULTIPLE RUNS?
	ORA	A	; 0=NO
	JNZ	MLOOP	; CONTINUE IF SO
	RET

;
;  SKIP TO NON-BLANK CHAR
;
SBLANK:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	SBLANK
	DCX	H	; PT TO NON-BLANK
	RET

;
;  PRINT HELP MESSAGE
;
PRHELP:
	CALL	BANNER	; PRINT BANNER
	CALL	PRINT
	DB	CR,LF
	DB	CR,LF,'COMPARE is used to quickly compare two files for '
	DB	'equality'
	DB	CR,LF
	DB	CR,LF,'COMPARE is invoked by a command like:'
	DB	CR,LF,'	COMPARE dir:file1.typ,dir:file2.typ M'
	DB	CR,LF,'where:'
	DB	CR,LF,'   "file1.typ" must be specified and is unambiguous'
	DB	CR,LF,'   "dir:" is an optional named directory (or DU:)'
	DB	CR,LF,'   "file2.typ" is optional and set equal to '
	DB	'"file1.typ" if omitted'
	DB	CR,LF,'   "M" is optional and allows Multiple Runs if given'
	DB	CR,LF
	DB	CR,LF,'Examples:'
	DB	CR,LF,'Command                  Files Compared'
	DB	CR,LF,'COMPARE T.COM,A1:        $$:T.COM, A1:T.COM'
	DB	CR,LF,'COMPARE A:T.COM          A$:T.COM, $$:T.COM'
	DB	CR,LF,'COMPARE A:T.COM,ROOT:    A$:T.COM, ROOT:T.COM'
	DB	CR,LF,'COMPARE A:T.COM,B:S.COM  A$:T.COM, B$:S.COM'
	DB	0
	RET

;
;  CHECK FOR ANY QUESTION MARKS FROM HL+1 TO HL+11
;  AFFECT ONLY AF REGISTERS IF OK
;
QCHECK:
	PUSH	H	; SAVE HL
	PUSH	B	; SAVE BC
	INX	H	; PT TO FIRST CHAR
	MVI	B,11	; 11 BYTES
	MVI	C,'?'	; SCAN FOR '?'
QC:
	MOV	A,M	; GET BYTE
	CMP	C	; '?'?
	JZ	QC1
	INX	H	; PT TO NEXT
	DCR	B	; COUNT DOWN
	JNZ	QC
	POP	B	; RESTORE
	POP	H
	RET
QC1:
	POP	B	; RESTORE AND ABORT
	POP	H
	POP	D	; CLEAR RETURN ADDRESS
	XCHG		; FCB PTR IN DE
	CALL	BANNER	; PRINT BANNER
	CALL	CRLF
	CALL	PRFN	; PRINT FILE NAME
	CALL	PRINT
	DB	'  Ambiguous File Name not Allowed',CR,LF,0
	RET

;
;  PRINT BANNER
;
BANNER:
	CALL	PRINT
	DB	'COMPARE  Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0'
	DB	0
	RET

;
;  PRINT NAMES OF SOURCE FILES
;    PRS1 -- SOURCE FILE 1
;    PRS2 -- SOURCE FILE 2
;
PRS1:
	CALL	PRINT
	DB	CR,LF,'Source File 1 -- ',0
	LXI	H,SDISK	; PT TO FIRST BYTE
	CALL	PRUD
	LXI	D,FCBS	; COMPUTE CRC FOR SOURCE FCB
	JMP	PRFN	; PRINT FILE NAME
PRS2:
	CALL	PRINT
	DB	CR,LF,'Source File 2 -- ',0
	LXI	H,DDISK	; PT TO FIRST BYTE
	CALL	PRUD
	LXI	D,FCBD	; COMPUTE CRC FOR DESTINATION FCB
	JMP	PRFN	; PRINT FILE NAME

;
;  MAIN VERIFY ROUTINE
;
VERIFY:
	CALL	PRS1	; PRINT SOURCE 1
	LDA	SDISK	; SELECT DISK AND USER
	MOV	B,A
	LDA	SUSER
	MOV	C,A
	CALL	LOGUD	; LOG IN DISK AND USER IN B,C
	CALL	CRCCLR	; CLEAR CRC
	CALL	COMPCRC	; READ IN FILE
	CALL	CRCDONE	; GET CRCK VALUE
	SHLD	CRCVAL	; SAVE IT
	LDA	BCNT	; GET OLD BLOCK COUNT
	STA	BCNT1	; SAVE IT
	CALL	PRS2	; PRINT NAME OF SOURCE 2
	LDA	DDISK	; SELECT DISK AND USER
	MOV	B,A
	LDA	DUSER
	MOV	C,A
	CALL	LOGUD	; LOG IN DISK AND USER IN B,C
	CALL	CRCCLR	; CLEAR CRC
	CALL	COMPCRC	; READ IN FILE
	LDA	BCNT	; GET BLOCK COUNT
	MOV	B,A	; RESULT IN B
	LDA	BCNT1	; CHECK FOR SAME SIZE
	CMP	B	; COMPARE BLOCK SIZES
	JNZ	NOMATCH	; NO MATCH IF NOT SAME
	LHLD	CRCVAL	; GET OLD CRC VALUE
	XCHG		; VALUE IN DE
	CALL	CRCDONE	; UPDATE COMPLETE
	CALL	COMPHD	; COMPARE HL TO DE
	JNZ	NOMATCH
	CALL	PRINT
	DB	CR,LF,'** Files are Identical **',0
	RET

;  VERIFY ERROR
NOMATCH:
	CALL	PRINT
	DB	CR,LF,'** Files are Different **',0
	RET

;
;  READ IN FILE AND COMPUTE ITS CRC; DE PTS TO FILE'S FCB
;
COMPCRC:
	CALL	CRCCLR	; CLEAR CRCK VALUE
	CALL	INITFCB	; INIT FCB FIELDS
	CALL	F$OPEN	; OPEN FILE
	CPI	0FFH	; FILE NOT FOUND?
	JNZ	LOAD	; LOAD DATA AND COMPUTE CRCVAL AND BCNT
	POP	H	; CLEAR 2 LEVELS OF RETURN ADDRESS
	POP	H
	CALL	CRLF
	CALL	PRFN	; PRINT FILE NAME
	CALL	PRINT
	DB	'  File Not Found',CR,LF,0
	RET

;
;  LOAD BUFFER FROM FILE WHOSE FCB IS PTED TO BY DE
;    ON OUTPUT, BCNT=NUMBER OF BLOCKS LOADED (UP TO 128) AND
;    CONT=0 IF DONE OR 128 IF NOT DONE
;
LOAD:
	XRA	A	; A=0
	STA	BCNT	; SET BLOCK COUNT

;  MAIN CRC EVALUATION LOOP
LOAD1:
	CALL	F$READ	; READ IN BLOCK
	ORA	A	; END OF FILE?
	JNZ	LOAD3	; RETURN
	LXI	H,BUFF	; PT TO BUFFER TO COMPUTE CRC FROM
	MVI	B,128	; COPY 128 BYTES
LOAD2:
	MOV	A,M	; GET BYTE
	CALL	CRCUPD	; UPDATE CRC
	INX	H	; PT TO NEXT
	DCR	B	; COUNT DOWN
	JNZ	LOAD2
	LDA	BCNT	; GET BLOCK COUNT
	INR	A	; INCREMENT IT
	STA	BCNT	; SET IT
	JMP	LOAD1	; CONTINUE
LOAD3:
	CALL	F$CLOSE	; CLOSE FILE
	RET

;
;  PRINT DISK/USER PTED TO BY HL (2 BYTES)
;
PRUD:
	MOV	A,M	; GET DISK
	ADI	'A'	; CONVERT TO LETTER
	CALL	COUT
	INX	H	; PT TO USER
	MOV	A,M	; GET USER
	CALL	PADC	; PRINT AS DEC
	CALL	PRINT
	DB	': ',0
	RET

;
;  PRINT FILE NAME WHOSE FCB IS PTED TO BY DE
;
PRFN:
	PUSH	H	; SAVE REGS
	PUSH	D
	PUSH	B
	XCHG		; FN PTED TO BY HL
	INX	H	; PT TO FIRST CHAR
	MVI	B,8	; 8 CHARS
	CALL	PRFN1
	MVI	A,'.'
	CALL	COUT
	MVI	B,3	; 3 CHARS FOR FILE TYPE
	CALL	PRFN1
	POP	B	; RESTORE REGS
	POP	D
	POP	H
	RET
PRFN1:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CALL	COUT	; PRINT
	DCR	B	; COUNT DOWN
	JNZ	PRFN1
	RET

;
;  BUFFERS
;
MULT:
	DS	1	; MULTIPLE RUN FLAG (0=NO MULT RUNS)
CDISK:
	DS	1	; CURRENT DISK
CUSER:
	DS	1	; CURRENT USER
SDISK:
	DS	1	; SOURCE DISK (MUST BE FOLLOWED BY SUSER)
SUSER:
	DS	1	; SOURCE USER
FCBS:
	DS	36	; SOURCE FCB
DDISK:
	DS	1	; DEST DISK (MUST BE FOLLOWED BY DUSER)
DUSER:
	DS	1	; DEST USER
FCBD:
	DS	36	; DESTINATION FCB
CRCVAL:
	DS	2	; CRC VALUE
CURDISK:
	DS	1	; CURRENT DISK NUMBER
BCNT:
	DS	1	; BUFFER COUNT
BCNT1:
	DS	1	; SECOND BUFFER COUNT
INLINE:
	DS	200	; INPUT LINE BUFFER

	END
